Introduction
Food prices play an important role in the overall Consumer Price
Index (CPI) calculations. Considering the CPI basket used to calculate
inflation rate or CPI, proportion of food products is important. In
addition, CPI is an overall generic calculation about price levels and
it may not be relevant for all households. That’s simply because for low
income families, the proportion of food related spendings is higher
compared to mid or high income families. Therefore, food price index
deserves a special interest.
In this analysis, I wanted to analyze characteristics of food price
index (FPI) of Canada and build a model to forecast food price index
values for 2023. This analysis is a simple time series analysis which
only considers past values of food price index. In the following
parts
Model Fitting
1. Required Packages
library(statcanR)
library(dplyr)
library(ggplot2)
library(magrittr)
library(forecast)
library(tseries)
library(ggTimeSeries)
library(stringr)
library(stats)
library(gridExtra)
library(FinTS)
library(nortsTest)
library(MTS)
library(TSstudio)
2. Data Collection from Statistics Canada
2.1. Data License
The data used in the analysis is directly imported from Statistics
Canada with an open license letting users to use, publish and freely
distribute the dataset.
2.2. Data Sharing API
Statistics Canada has an API for data sharing and using the R package
statcanR, data can be directly retrieved from StatCan
by only using table id. The imported data is in normalized database
table format and therefore there is no need to data transformation or
wrangling.
2.3. Data Collection
Statistics Canada has Consumer Price Index, monthly,
seasonally adjusted dataset, which contains overall CPI
and price indexes for sub-categories like food. This dataset can be
accessed with the table id = 18-10-0006-01.
df = statcan_data('18-10-0006-01', 'eng')
names(df) = str_replace_all(names(df), c(" " = "_"))
fpi = df %>%
filter(Products_and_product_groups == 'Food') %>%
select(c(REF_DATE, VALUE))
fpi = ts(fpi$VALUE, start=c(1992,1), frequency = 12)
rm(df)
3. Data Visualization
plot.ts(fpi,
main = 'Food Price Index of Canada',
sub = 'From 1992 to Present',
col = 'darkred',
xy.lines = TRUE,
xlab = 'Time',
ylab = 'Index Value',
lwd = 3)
abline(reg=lm(fpi~time(fpi)), col = 'darkgrey', lwd = 2)

Above plot exhibits and obvious increasing trend for FPI, which can
be seen with the fitted grey linear trend line. In addition, the changes
in FPI are not constant. For some periods, the line is flatter compared
to drastic increase period like 2021 and 2022.
This dataset is seasonally adjusted but in the following part, I
tried to decompose the FPI and get trend and seasonal components
together with the random (residual) part.
4. Decomposition of CPI
decomposed_fpi <- tslm(fpi ~ trend + fourier(fpi, 2))
trend <- coef(decomposed_fpi)[1] + coef(decomposed_fpi)['trend']*seq_along(fpi)
components <- cbind(
FPI = fpi,
trend = trend,
seasonal = fpi - trend - residuals(decomposed_fpi),
residuals = residuals(decomposed_fpi))
autoplot(components, facet=TRUE)

There are 4 subplots in the above visual and the first one is the sum
of the last three meaning that FPI has a trend, a seasonal and an
unexplained residual component. This is interesting because I would not
expect any seasonal component since the data was seasonally
adjusted.
Another interesting result is that we cannot only explain the
variation in this time series with trend and seasonality: residuals seem
to be non-random and for some periods, they appear to be
autocorrelated.
5. Distribution of Food Price Index
5.1. Visuals
hist(fpi,
freq = FALSE,
main = 'Histogram of Food Price Index',
xlab = 'Food Price Index',
col = 'darkred')

The distribution of FPI (as is) is not even close to normal
distribution.
hist(log(fpi),
freq = FALSE,
main = 'Histogram of log(Food Price Index)',
xlab = 'log(Food Price Index)',
col = 'darkgrey')

Above visual is the distribution of natural logartihm of FPI and even
the log transformed version is not normally distributed.
hist(diff(log(fpi)),
freq = FALSE,
main = 'Histogram of diff(log(Food Price Index))',
xlab = 'diff(log(Food Price Index))',
col = 'darkgrey')

In addition to the log transformation, the monthly differences in FPI
were calculated and plotted in above visual. The distribution shape of
the log differenced FPI is better than the former two trials and very
close to normal.
5.2. Statistical Tests for Normality
Although visuals play great role in understanding the distribution of
time series, formally statistical tests are required to check the
normality of the variables. In the following part, I used Jarque-Bera
Test to statistically check whether the time series is normally
distributed or not. In these statistical tests, following hypotheses
were used:
\[
\begin{align*}
H_0: & \text{The distribution of FPI IS NOT statistically different
than normal distribution} \\
H_A: & \text{The distribution of FPI IS statistically different than
normal distribution}
\end{align*}
\]
jarque.bera.test(fpi)
Jarque Bera Test
data: fpi
X-squared = 24.796, df = 2, p-value = 4.128e-06
jarque.bera.test(log(fpi))
Jarque Bera Test
data: log(fpi)
X-squared = 26.026, df = 2, p-value = 2.231e-06
jarque.bera.test(diff(log(fpi)))
Jarque Bera Test
data: diff(log(fpi))
X-squared = 32.618, df = 2, p-value = 8.26e-08
FPI, log(FPI) and diff(log(FPI)) were tested and all tests resulted
in \(p-value < \alpha = 0.05\)
meaning the rejection of \(H_0\), which
states normal distribution of FPI. Therefore, the conclusion is that
the time series is not normally distributed.
6. Testing Stationary (White Noise)
6.1. Visuals
6.1.1. Lag Plot to Check Autocorrelation
gglagplot(x = log(fpi), lag=12, seasonal = TRUE) +
scale_color_viridis_d(option = "A") +
theme_bw()

The scatterplot grid presented above indicates that log(FPI) is
significantly correlated with its lags.
gglagplot(x = diff(log(fpi)), lag=12, seasonal = TRUE) +
scale_color_viridis_d(option = "A") +
theme_bw()

Instead of log(FPI), I used diff(log(FPI)) to check for
autocorrelation. This time, the log differenced FPI showed no
significant autocorrelation with lag values.
6.1.2. Autocorrelation and Partial Autocorrelation Plots (ACF &
PACF)
fpi_acf = ggAcf(log(fpi), lag.max = 50, plot = TRUE) +
labs(title = "log(FPI) ACF")+
theme_classic()
fpi_pacf = ggPacf(log(fpi), lag.max = 50, plot = TRUE) +
labs(title = "log(FPI) PACF")+
theme_classic()
grid.arrange(fpi_acf, fpi_pacf, nrow = 1)

ACF and PACF are important visuals to check while fitting time series
models. Considering the plotted ACF and PACF plotted above, I can state
that log(FPI) follows a MA process instead of an AR process since bars
in ACF slightly decreases with increasing lags but bars in PACF directly
becomes insignificant after first one.
6.2. Statistical Tests for Stationary (White Noise)
Similar to the normality test, in addition to the visuals, I used
statistical test to detect stationary (unit root). Following hypotheses
were used in these tests.
\[
\begin{align*}
H_0: & \text{Non Stationary (Unit Root present)} \\
H_A: & \text{Stationary (Unit Root NOT present)}
\end{align*}
\]
adf.test(log(fpi), alternative = 'stationary')
Augmented Dickey-Fuller Test
data: log(fpi)
Dickey-Fuller = -2.2, Lag order = 7, p-value = 0.4926
alternative hypothesis: stationary
adf.test(diff(log(fpi)), alternative = 'stationary')
Warning: p-value smaller than printed p-value
Augmented Dickey-Fuller Test
data: diff(log(fpi))
Dickey-Fuller = -5.1073, Lag order = 7, p-value = 0.01
alternative hypothesis: stationary
Statistical test results supports the visuals discussed in the
previous part:
For log(FPI), the calculated \(p-value
> \alpha = 0.05\) meaning that \(H_0\) cannot be rejected. The conclusion
for log(FPI) is that it has unit root (or it is not stationary). This is
not surprising since the original FPI data had a trend component
(refer to decomposition part discussed above).
For log differenced FPI, my conclusion is the opposite since the
calculated \(p-value < \alpha =
0.05\) meaning that \(H_0\) must
be be rejected. The conclusion for diff(log(FPI)) is that it does not
have unit root (or it is stationary).
In above part, I discussed about FPI (and log(FPI)) follow a MA
process instead of an AR process. In addition to that, statistical test
results discussed in this part also suggest that there will be an I
component in the ARIMA process because with the integrated differencing,
data become stationary.
7. ARIMA (or SARIMA) Model
7.1. Fitting the model
A general ARIMA model requires three parameters:
p for AR component: p refers to number of lag values to include
in the model
d for I component: d refers to differencing window
q for MA component: q refers to the number of past residuals to
include in the model.
Remember, in the decomposition part, I discussed about the surprising
seasonal component of FPI. Therefore, my expectation is that the fitted
model will be SARIMA (seasonal ARIMA), which requires three additional
parameters (P,D,Q same meaning with above parameters but this time
seasonal).
The optimal parameters can be decided with trials and then comparing
the AIC or BIC values (the lowest should be chosen) of the models.
However, auto.arima() function exactly does the same thing and therefore
I prefer to use it to be practical.
Note: Since differencing can be handled within
the ARIMA (or SARIMA) process by setting d>0, I used log(FPI) inside
the function. auto.arima will choose a non-zero value for d to manage
differencing.
ARIMA_fpi = auto.arima(log(fpi))
summary(ARIMA_fpi)
Series: log(fpi)
ARIMA(0,2,1)(0,0,2)[12]
Coefficients:
ma1 sma1 sma2
-0.9022 -0.2051 -0.2649
s.e. 0.0287 0.0526 0.0502
sigma^2 = 1.224e-05: log likelihood = 1568.27
AIC=-3128.54 AICc=-3128.43 BIC=-3112.89
Training set error measures:
ME RMSE MAE MPE MAPE MASE ACF1
Training set 0.0001044633 0.003475398 0.002661481 0.001965791 0.05641366 0.1075394 -0.04868011
Similar to my discussion in former sections of this paper,
auto.arima() function resulted in a SARIMA(0,2,1)(0,0,1) model. As
discussed above,
There is no AR component
There is I component with d = 2
There is MA component with q = 1
There is seasonal MA with Q = 1
8. Model Diagnostics
tsdiag(ARIMA_fpi)

The residual plot of the fitted model indicates signs of non-constant
variance since there are picks in the residuals in some periods compared
to other periods. This shows the variation in the residuals are not
constant.
checkresiduals(ARIMA_fpi, test = FALSE)

The ACF of residuals look acceptable and the distribution of
residuals is close to normal distribution.
autoplot(ARIMA_fpi)

All the dots are inside the circle, which is an indication of an
acceptable model.
archTest(residuals(ARIMA_fpi))
Q(m) of squared series(LM test):
Test statistic: 18.60339 p-value: 0.04559924
Rank-based Test:
Test statistic: 17.58444 p-value: 0.0623914
In order to test for heteroscedasticity, I used archTest from MTS
package (which uses Ljung-Box test). The \(p-value\) of the rank-based test is greater
than \(\alpha = 0.05\) but the normal
test has a \(p-value < \alpha =
0.05\).
\[
\begin{align*}
H_0: & \text{No autocorrelation of residual squares (Residuals are
random)} \\
H_A: & \text{Autocorrelation of residual squares (Residuals are NOT
random)}
\end{align*}
\]
According to the package definition for archTest(), the rank series
of the squared time series is than used to test the conditional
heteroscedasticity. Our model is successful in terms of rank-based test
model and therefore, we can make forecasts based on the existing model.
However, the p-values are very close to the critical levels and
therefore, there is a need for ARCH or GARCH models to predict the
variance.
Forecasting
fpi_forecast = forecast(ARIMA_fpi, h = 12, level = c(95,99))
fpi_forecast$x = exp(fpi_forecast$x)
fpi_forecast$mean = exp(fpi_forecast$mean)
fpi_forecast$lower = exp(fpi_forecast$lower)
fpi_forecast$upper = exp(fpi_forecast$upper)
# using plotly in R to plot interactive visuals.
plot_forecast(fpi_forecast,
title = 'Food Price Index Forecast for 2023',
Xtitle = 'Time',
Ytitle = 'Food Price Index',
color = NULL,
width = 2)
Conclusion
Canada’s CPI for food (or FPI) follows a seasonal ARIMA process.
Following the tests implemented to ensure meeting the assumptions, I
fitted a seasonal ARIMA model to forecast FPI values for 2023. My
forecasts indicates that food prices in Canada are likely to increase
further in 2023. Furthermore, I plan to fit an ARCH or GARCH model for
the conditional heteroscedasticity as the next step of this paper.
References
Roser, Max, and Hannah Ritchie. (2021) Food Prices. Our
World in Data, ourworldindata.org. Available at: https://ourworldindata.org/food-prices. (Accessed:
January 18, 2023).
Bogmans, C., Pescatori, A. and Prifti, E. (2021) Four facts about
soaring consumer food prices, IMF. Available at: https://www.imf.org/en/Blogs/Articles/2021/06/24/four-facts-about-soaring-consumer-food-prices
(Accessed: January 18, 2023).
Fradella, A. (2022) Behind the Numbers: What’s Causing Growth in
Food Prices, Government of Canada, Statistics Canada. Available at:
https://www150.statcan.gc.ca/n1/pub/62f0014m/62f0014m2022014-eng.htm
(Accessed: January 18, 2023).
Statistics Canada (2023) Consumer price index, monthly,
seasonally adjusted, Consumer Price Index, monthly, seasonally
adjusted. Government of Canada, Statistics Canada. Available at: https://www150.statcan.gc.ca/t1/tbl1/en/tv.action?pid=1810000601
(Accessed: January 18, 2023).
Statistics Canada (2022) Statistics Canada Open Licence,
Government of Canada, Statistics Canada. Government of Canada,
Statistics Canada. Available at: https://www.statcan.gc.ca/en/reference/licence
(Accessed: January 18, 2023).
Statistics Canada (2022) Web data service (WDS),
Government of Canada, Statistics Canada. Government of Canada,
Statistics Canada. Available at: https://www.statcan.gc.ca/en/developers/wds (Accessed:
January 18, 2023).
LS0tDQp0aXRsZTogIkZvb2QgUHJpY2UgSW5kZXggb2YgQ2FuYWRhIg0Kc3VidGl0bGU6ICJVbml2YXJpYXRlIFRpbWUgU2VyaWVzIEFuYWx5c2lzIHRvIGZvcmVjYXN0IGZ1dHVyZSBGb29kIFByaWNlIEluZGV4IHZhbHVlcyINCmF1dGhvcjogIk8uIFRheWxhbiBDaWNlaywgVGhlIFVuaXZlcnNpdHkgb2YgQ2FsZ2FyeSINCmRhdGU6IEphbiAxOCwgMjAyMw0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBJbnRyb2R1Y3Rpb24NCg0KRm9vZCBwcmljZXMgcGxheSBhbiBpbXBvcnRhbnQgcm9sZSBpbiB0aGUgb3ZlcmFsbCBDb25zdW1lciBQcmljZSBJbmRleCAoQ1BJKSBjYWxjdWxhdGlvbnMuIENvbnNpZGVyaW5nIHRoZSBDUEkgYmFza2V0IHVzZWQgdG8gY2FsY3VsYXRlIGluZmxhdGlvbiByYXRlIG9yIENQSSwgcHJvcG9ydGlvbiBvZiBmb29kIHByb2R1Y3RzIGlzIGltcG9ydGFudC4gSW4gYWRkaXRpb24sIENQSSBpcyBhbiBvdmVyYWxsIGdlbmVyaWMgY2FsY3VsYXRpb24gYWJvdXQgcHJpY2UgbGV2ZWxzIGFuZCBpdCBtYXkgbm90IGJlIHJlbGV2YW50IGZvciBhbGwgaG91c2Vob2xkcy4gVGhhdCdzIHNpbXBseSBiZWNhdXNlIGZvciBsb3cgaW5jb21lIGZhbWlsaWVzLCB0aGUgcHJvcG9ydGlvbiBvZiBmb29kIHJlbGF0ZWQgc3BlbmRpbmdzIGlzIGhpZ2hlciBjb21wYXJlZCB0byBtaWQgb3IgaGlnaCBpbmNvbWUgZmFtaWxpZXMuIFRoZXJlZm9yZSwgZm9vZCBwcmljZSBpbmRleCBkZXNlcnZlcyBhIHNwZWNpYWwgaW50ZXJlc3QuDQoNCkluIHRoaXMgYW5hbHlzaXMsIEkgd2FudGVkIHRvIGFuYWx5emUgY2hhcmFjdGVyaXN0aWNzIG9mIGZvb2QgcHJpY2UgaW5kZXggKEZQSSkgb2YgQ2FuYWRhIGFuZCBidWlsZCBhIG1vZGVsIHRvIGZvcmVjYXN0IGZvb2QgcHJpY2UgaW5kZXggdmFsdWVzIGZvciAyMDIzLiBUaGlzIGFuYWx5c2lzIGlzIGEgc2ltcGxlIHRpbWUgc2VyaWVzIGFuYWx5c2lzIHdoaWNoIG9ubHkgY29uc2lkZXJzIHBhc3QgdmFsdWVzIG9mIGZvb2QgcHJpY2UgaW5kZXguIEluIHRoZSBmb2xsb3dpbmcgcGFydHMNCg0KIyBNb2RlbCBGaXR0aW5nDQoNCiMjIDEuIFJlcXVpcmVkIFBhY2thZ2VzDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KHN0YXRjYW5SKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeSh0c2VyaWVzKQ0KbGlicmFyeShnZ1RpbWVTZXJpZXMpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHN0YXRzKQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KEZpblRTKQ0KbGlicmFyeShub3J0c1Rlc3QpDQpsaWJyYXJ5KE1UUykNCmxpYnJhcnkoVFNzdHVkaW8pDQpgYGANCg0KIyMgMi4gRGF0YSBDb2xsZWN0aW9uIGZyb20gU3RhdGlzdGljcyBDYW5hZGENCg0KIyMjIDIuMS4gRGF0YSBMaWNlbnNlDQoNClRoZSBkYXRhIHVzZWQgaW4gdGhlIGFuYWx5c2lzIGlzIGRpcmVjdGx5IGltcG9ydGVkIGZyb20gU3RhdGlzdGljcyBDYW5hZGEgd2l0aCBhbiBvcGVuIGxpY2Vuc2UgbGV0dGluZyB1c2VycyB0byB1c2UsIHB1Ymxpc2ggYW5kIGZyZWVseSBkaXN0cmlidXRlIHRoZSBkYXRhc2V0Lg0KDQojIyMgMi4yLiBEYXRhIFNoYXJpbmcgQVBJDQoNClN0YXRpc3RpY3MgQ2FuYWRhIGhhcyBhbiBBUEkgZm9yIGRhdGEgc2hhcmluZyBhbmQgdXNpbmcgdGhlIFIgcGFja2FnZSAqKnN0YXRjYW5SKiosIGRhdGEgY2FuIGJlIGRpcmVjdGx5IHJldHJpZXZlZCBmcm9tIFN0YXRDYW4gYnkgb25seSB1c2luZyB0YWJsZSBpZC4gVGhlIGltcG9ydGVkIGRhdGEgaXMgaW4gbm9ybWFsaXplZCBkYXRhYmFzZSB0YWJsZSBmb3JtYXQgYW5kIHRoZXJlZm9yZSB0aGVyZSBpcyBubyBuZWVkIHRvIGRhdGEgdHJhbnNmb3JtYXRpb24gb3Igd3JhbmdsaW5nLg0KDQojIyMgMi4zLiBEYXRhIENvbGxlY3Rpb24NCg0KU3RhdGlzdGljcyBDYW5hZGEgaGFzICoqKkNvbnN1bWVyIFByaWNlIEluZGV4LCBtb250aGx5LCBzZWFzb25hbGx5IGFkanVzdGVkKioqIGRhdGFzZXQsIHdoaWNoIGNvbnRhaW5zIG92ZXJhbGwgQ1BJIGFuZCBwcmljZSBpbmRleGVzIGZvciBzdWItY2F0ZWdvcmllcyBsaWtlIGZvb2QuIFRoaXMgZGF0YXNldCBjYW4gYmUgYWNjZXNzZWQgd2l0aCB0aGUgdGFibGUgaWQgPSAxOC0xMC0wMDA2LTAxLg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KZGYgPSBzdGF0Y2FuX2RhdGEoJzE4LTEwLTAwMDYtMDEnLCAnZW5nJykNCm5hbWVzKGRmKSA9IHN0cl9yZXBsYWNlX2FsbChuYW1lcyhkZiksIGMoIiAiID0gIl8iKSkNCmZwaSA9IGRmICU+JSANCiAgZmlsdGVyKFByb2R1Y3RzX2FuZF9wcm9kdWN0X2dyb3VwcyA9PSAnRm9vZCcpICU+JSANCiAgc2VsZWN0KGMoUkVGX0RBVEUsIFZBTFVFKSkNCmZwaSA9IHRzKGZwaSRWQUxVRSwgc3RhcnQ9YygxOTkyLDEpLCBmcmVxdWVuY3kgPSAxMikNCnJtKGRmKQ0KYGBgDQoNCiMjIDMuIERhdGEgVmlzdWFsaXphdGlvbg0KDQpgYGB7cn0NCnBsb3QudHMoZnBpLA0KICAgICAgICBtYWluID0gJ0Zvb2QgUHJpY2UgSW5kZXggb2YgQ2FuYWRhJywNCiAgICAgICAgc3ViID0gJ0Zyb20gMTk5MiB0byBQcmVzZW50JywNCiAgICAgICAgY29sID0gJ2RhcmtyZWQnLA0KICAgICAgICB4eS5saW5lcyA9IFRSVUUsDQogICAgICAgIHhsYWIgPSAnVGltZScsDQogICAgICAgIHlsYWIgPSAnSW5kZXggVmFsdWUnLA0KICAgICAgICBsd2QgPSAzKQ0KYWJsaW5lKHJlZz1sbShmcGl+dGltZShmcGkpKSwgY29sID0gJ2RhcmtncmV5JywgbHdkID0gMikNCmBgYA0KDQpBYm92ZSBwbG90IGV4aGliaXRzIGFuZCBvYnZpb3VzIGluY3JlYXNpbmcgdHJlbmQgZm9yIEZQSSwgd2hpY2ggY2FuIGJlIHNlZW4gd2l0aCB0aGUgZml0dGVkIGdyZXkgbGluZWFyIHRyZW5kIGxpbmUuIEluIGFkZGl0aW9uLCB0aGUgY2hhbmdlcyBpbiBGUEkgYXJlIG5vdCBjb25zdGFudC4gRm9yIHNvbWUgcGVyaW9kcywgdGhlIGxpbmUgaXMgZmxhdHRlciBjb21wYXJlZCB0byBkcmFzdGljIGluY3JlYXNlIHBlcmlvZCBsaWtlIDIwMjEgYW5kIDIwMjIuDQoNClRoaXMgZGF0YXNldCBpcyBzZWFzb25hbGx5IGFkanVzdGVkIGJ1dCBpbiB0aGUgZm9sbG93aW5nIHBhcnQsIEkgdHJpZWQgdG8gZGVjb21wb3NlIHRoZSBGUEkgYW5kIGdldCB0cmVuZCBhbmQgc2Vhc29uYWwgY29tcG9uZW50cyB0b2dldGhlciB3aXRoIHRoZSByYW5kb20gKHJlc2lkdWFsKSBwYXJ0Lg0KDQojIyAqKjQuIERlY29tcG9zaXRpb24gb2YgQ1BJKioNCg0KYGBge3J9DQpkZWNvbXBvc2VkX2ZwaSA8LSB0c2xtKGZwaSB+IHRyZW5kICsgZm91cmllcihmcGksIDIpKQ0KDQp0cmVuZCA8LSBjb2VmKGRlY29tcG9zZWRfZnBpKVsxXSArIGNvZWYoZGVjb21wb3NlZF9mcGkpWyd0cmVuZCddKnNlcV9hbG9uZyhmcGkpDQoNCmNvbXBvbmVudHMgPC0gY2JpbmQoDQogIEZQSSA9IGZwaSwNCiAgdHJlbmQgPSB0cmVuZCwNCiAgc2Vhc29uYWwgPSBmcGkgLSB0cmVuZCAtIHJlc2lkdWFscyhkZWNvbXBvc2VkX2ZwaSksDQogIHJlc2lkdWFscyA9IHJlc2lkdWFscyhkZWNvbXBvc2VkX2ZwaSkpDQphdXRvcGxvdChjb21wb25lbnRzLCBmYWNldD1UUlVFKQ0KYGBgDQoNClRoZXJlIGFyZSA0IHN1YnBsb3RzIGluIHRoZSBhYm92ZSB2aXN1YWwgYW5kIHRoZSBmaXJzdCBvbmUgaXMgdGhlIHN1bSBvZiB0aGUgbGFzdCB0aHJlZSBtZWFuaW5nIHRoYXQgRlBJIGhhcyBhIHRyZW5kLCBhIHNlYXNvbmFsIGFuZCBhbiB1bmV4cGxhaW5lZCByZXNpZHVhbCBjb21wb25lbnQuIFRoaXMgaXMgaW50ZXJlc3RpbmcgYmVjYXVzZSBJIHdvdWxkIG5vdCBleHBlY3QgYW55IHNlYXNvbmFsIGNvbXBvbmVudCBzaW5jZSB0aGUgZGF0YSB3YXMgc2Vhc29uYWxseSBhZGp1c3RlZC4NCg0KQW5vdGhlciBpbnRlcmVzdGluZyByZXN1bHQgaXMgdGhhdCB3ZSBjYW5ub3Qgb25seSBleHBsYWluIHRoZSB2YXJpYXRpb24gaW4gdGhpcyB0aW1lIHNlcmllcyB3aXRoIHRyZW5kIGFuZCBzZWFzb25hbGl0eTogcmVzaWR1YWxzIHNlZW0gdG8gYmUgbm9uLXJhbmRvbSBhbmQgZm9yIHNvbWUgcGVyaW9kcywgdGhleSBhcHBlYXIgdG8gYmUgYXV0b2NvcnJlbGF0ZWQuDQoNCiMjIDUuIERpc3RyaWJ1dGlvbiBvZiBGb29kIFByaWNlIEluZGV4DQoNCiMjIyA1LjEuIFZpc3VhbHMNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmhpc3QoZnBpLA0KICAgICBmcmVxID0gRkFMU0UsDQogICAgIG1haW4gPSAnSGlzdG9ncmFtIG9mIEZvb2QgUHJpY2UgSW5kZXgnLA0KICAgICB4bGFiID0gJ0Zvb2QgUHJpY2UgSW5kZXgnLA0KICAgICBjb2wgPSAnZGFya3JlZCcpDQpgYGANCg0KVGhlIGRpc3RyaWJ1dGlvbiBvZiBGUEkgKGFzIGlzKSBpcyBub3QgZXZlbiBjbG9zZSB0byBub3JtYWwgZGlzdHJpYnV0aW9uLg0KDQpgYGB7cn0NCmhpc3QobG9nKGZwaSksDQogICAgIGZyZXEgPSBGQUxTRSwNCiAgICAgbWFpbiA9ICdIaXN0b2dyYW0gb2YgbG9nKEZvb2QgUHJpY2UgSW5kZXgpJywNCiAgICAgeGxhYiA9ICdsb2coRm9vZCBQcmljZSBJbmRleCknLA0KICAgICBjb2wgPSAnZGFya2dyZXknKQ0KYGBgDQoNCkFib3ZlIHZpc3VhbCBpcyB0aGUgZGlzdHJpYnV0aW9uIG9mIG5hdHVyYWwgbG9nYXJ0aWhtIG9mIEZQSSBhbmQgZXZlbiB0aGUgbG9nIHRyYW5zZm9ybWVkIHZlcnNpb24gaXMgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLg0KDQpgYGB7cn0NCmhpc3QoZGlmZihsb2coZnBpKSksDQogICAgIGZyZXEgPSBGQUxTRSwNCiAgICAgbWFpbiA9ICdIaXN0b2dyYW0gb2YgZGlmZihsb2coRm9vZCBQcmljZSBJbmRleCkpJywNCiAgICAgeGxhYiA9ICdkaWZmKGxvZyhGb29kIFByaWNlIEluZGV4KSknLA0KICAgICBjb2wgPSAnZGFya2dyZXknKQ0KYGBgDQoNCkluIGFkZGl0aW9uIHRvIHRoZSBsb2cgdHJhbnNmb3JtYXRpb24sIHRoZSBtb250aGx5IGRpZmZlcmVuY2VzIGluIEZQSSB3ZXJlIGNhbGN1bGF0ZWQgYW5kIHBsb3R0ZWQgaW4gYWJvdmUgdmlzdWFsLiBUaGUgZGlzdHJpYnV0aW9uIHNoYXBlIG9mIHRoZSBsb2cgZGlmZmVyZW5jZWQgRlBJIGlzIGJldHRlciB0aGFuIHRoZSBmb3JtZXIgdHdvIHRyaWFscyBhbmQgdmVyeSBjbG9zZSB0byBub3JtYWwuDQoNCiMjIyA1LjIuIFN0YXRpc3RpY2FsIFRlc3RzIGZvciBOb3JtYWxpdHkNCg0KQWx0aG91Z2ggdmlzdWFscyBwbGF5IGdyZWF0IHJvbGUgaW4gdW5kZXJzdGFuZGluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRpbWUgc2VyaWVzLCBmb3JtYWxseSBzdGF0aXN0aWNhbCB0ZXN0cyBhcmUgcmVxdWlyZWQgdG8gY2hlY2sgdGhlIG5vcm1hbGl0eSBvZiB0aGUgdmFyaWFibGVzLiBJbiB0aGUgZm9sbG93aW5nIHBhcnQsIEkgdXNlZCBKYXJxdWUtQmVyYSBUZXN0IHRvIHN0YXRpc3RpY2FsbHkgY2hlY2sgd2hldGhlciB0aGUgdGltZSBzZXJpZXMgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgb3Igbm90LiBJbiB0aGVzZSBzdGF0aXN0aWNhbCB0ZXN0cywgZm9sbG93aW5nIGh5cG90aGVzZXMgd2VyZSB1c2VkOg0KDQokJA0KXGJlZ2lue2FsaWduKn0gDQpIXzA6ICYgXHRleHR7VGhlIGRpc3RyaWJ1dGlvbiBvZiBGUEkgSVMgTk9UIHN0YXRpc3RpY2FsbHkgZGlmZmVyZW50IHRoYW4gbm9ybWFsIGRpc3RyaWJ1dGlvbn0gXFwNCkhfQTogJiBcdGV4dHtUaGUgZGlzdHJpYnV0aW9uIG9mIEZQSSBJUyBzdGF0aXN0aWNhbGx5IGRpZmZlcmVudCB0aGFuIG5vcm1hbCBkaXN0cmlidXRpb259DQpcZW5ke2FsaWduKn0NCiQkDQoNCmBgYHtyfQ0KamFycXVlLmJlcmEudGVzdChmcGkpDQpgYGANCg0KYGBge3J9DQpqYXJxdWUuYmVyYS50ZXN0KGxvZyhmcGkpKQ0KYGBgDQoNCmBgYHtyfQ0KamFycXVlLmJlcmEudGVzdChkaWZmKGxvZyhmcGkpKSkNCmBgYA0KDQpGUEksIGxvZyhGUEkpIGFuZCBkaWZmKGxvZyhGUEkpKSB3ZXJlIHRlc3RlZCBhbmQgYWxsIHRlc3RzIHJlc3VsdGVkIGluICRwLXZhbHVlIDwgXGFscGhhID0gMC4wNSQgbWVhbmluZyB0aGUgcmVqZWN0aW9uIG9mICRIXzAkLCB3aGljaCBzdGF0ZXMgbm9ybWFsIGRpc3RyaWJ1dGlvbiBvZiBGUEkuIFRoZXJlZm9yZSwgdGhlIGNvbmNsdXNpb24gaXMgdGhhdCAqKnRoZSB0aW1lIHNlcmllcyBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQqKi4NCg0KIyMgNi4gVGVzdGluZyBTdGF0aW9uYXJ5IChXaGl0ZSBOb2lzZSkNCg0KIyMjIDYuMS4gVmlzdWFscw0KDQojIyMjIDYuMS4xLiBMYWcgUGxvdCB0byBDaGVjayBBdXRvY29ycmVsYXRpb24NCg0KYGBge3J9DQpnZ2xhZ3Bsb3QoeCA9IGxvZyhmcGkpLCBsYWc9MTIsIHNlYXNvbmFsID0gVFJVRSkgKyANCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKG9wdGlvbiA9ICJBIikgKw0KICB0aGVtZV9idygpDQpgYGANCg0KVGhlIHNjYXR0ZXJwbG90IGdyaWQgcHJlc2VudGVkIGFib3ZlIGluZGljYXRlcyB0aGF0IGxvZyhGUEkpIGlzIHNpZ25pZmljYW50bHkgY29ycmVsYXRlZCB3aXRoIGl0cyBsYWdzLg0KDQpgYGB7cn0NCmdnbGFncGxvdCh4ID0gZGlmZihsb2coZnBpKSksIGxhZz0xMiwgc2Vhc29uYWwgPSBUUlVFKSArIA0KICBzY2FsZV9jb2xvcl92aXJpZGlzX2Qob3B0aW9uID0gIkEiKSArDQogIHRoZW1lX2J3KCkNCmBgYA0KDQpJbnN0ZWFkIG9mIGxvZyhGUEkpLCBJIHVzZWQgZGlmZihsb2coRlBJKSkgdG8gY2hlY2sgZm9yIGF1dG9jb3JyZWxhdGlvbi4gVGhpcyB0aW1lLCB0aGUgbG9nIGRpZmZlcmVuY2VkIEZQSSBzaG93ZWQgbm8gc2lnbmlmaWNhbnQgYXV0b2NvcnJlbGF0aW9uIHdpdGggbGFnIHZhbHVlcy4NCg0KIyMjIyA2LjEuMi4gQXV0b2NvcnJlbGF0aW9uIGFuZCBQYXJ0aWFsIEF1dG9jb3JyZWxhdGlvbiBQbG90cyAoQUNGICYgUEFDRikNCg0KYGBge3J9DQpmcGlfYWNmID0gZ2dBY2YobG9nKGZwaSksIGxhZy5tYXggPSA1MCwgcGxvdCA9IFRSVUUpICsgDQogIGxhYnModGl0bGUgPSAibG9nKEZQSSkgQUNGIikrDQogIHRoZW1lX2NsYXNzaWMoKQ0KZnBpX3BhY2YgPSBnZ1BhY2YobG9nKGZwaSksIGxhZy5tYXggPSA1MCwgcGxvdCA9IFRSVUUpICsNCiAgbGFicyh0aXRsZSA9ICJsb2coRlBJKSBQQUNGIikrDQogIHRoZW1lX2NsYXNzaWMoKQ0KZ3JpZC5hcnJhbmdlKGZwaV9hY2YsIGZwaV9wYWNmLCBucm93ID0gMSkNCmBgYA0KDQpBQ0YgYW5kIFBBQ0YgYXJlIGltcG9ydGFudCB2aXN1YWxzIHRvIGNoZWNrIHdoaWxlIGZpdHRpbmcgdGltZSBzZXJpZXMgbW9kZWxzLiBDb25zaWRlcmluZyB0aGUgcGxvdHRlZCBBQ0YgYW5kIFBBQ0YgcGxvdHRlZCBhYm92ZSwgSSBjYW4gc3RhdGUgdGhhdCBsb2coRlBJKSBmb2xsb3dzIGEgTUEgcHJvY2VzcyBpbnN0ZWFkIG9mIGFuIEFSIHByb2Nlc3Mgc2luY2UgYmFycyBpbiBBQ0Ygc2xpZ2h0bHkgZGVjcmVhc2VzIHdpdGggaW5jcmVhc2luZyBsYWdzIGJ1dCBiYXJzIGluIFBBQ0YgZGlyZWN0bHkgYmVjb21lcyBpbnNpZ25pZmljYW50IGFmdGVyIGZpcnN0IG9uZS4NCg0KIyMjIDYuMi4gU3RhdGlzdGljYWwgVGVzdHMgZm9yIFN0YXRpb25hcnkgKFdoaXRlIE5vaXNlKQ0KDQpTaW1pbGFyIHRvIHRoZSBub3JtYWxpdHkgdGVzdCwgaW4gYWRkaXRpb24gdG8gdGhlIHZpc3VhbHMsIEkgdXNlZCBzdGF0aXN0aWNhbCB0ZXN0IHRvIGRldGVjdCBzdGF0aW9uYXJ5ICh1bml0IHJvb3QpLiBGb2xsb3dpbmcgaHlwb3RoZXNlcyB3ZXJlIHVzZWQgaW4gdGhlc2UgdGVzdHMuDQoNCiQkDQpcYmVnaW57YWxpZ24qfSANCkhfMDogJiBcdGV4dHtOb24gU3RhdGlvbmFyeSAoVW5pdCBSb290IHByZXNlbnQpfSBcXA0KSF9BOiAmIFx0ZXh0e1N0YXRpb25hcnkgKFVuaXQgUm9vdCBOT1QgcHJlc2VudCl9DQpcZW5ke2FsaWduKn0NCiQkDQoNCmBgYHtyfQ0KYWRmLnRlc3QobG9nKGZwaSksIGFsdGVybmF0aXZlID0gJ3N0YXRpb25hcnknKQ0KYGBgDQoNCmBgYHtyfQ0KYWRmLnRlc3QoZGlmZihsb2coZnBpKSksIGFsdGVybmF0aXZlID0gJ3N0YXRpb25hcnknKQ0KYGBgDQoNClN0YXRpc3RpY2FsIHRlc3QgcmVzdWx0cyBzdXBwb3J0cyB0aGUgdmlzdWFscyBkaXNjdXNzZWQgaW4gdGhlIHByZXZpb3VzIHBhcnQ6DQoNCi0gICBGb3IgbG9nKEZQSSksIHRoZSBjYWxjdWxhdGVkICRwLXZhbHVlID4gXGFscGhhID0gMC4wNSQgbWVhbmluZyB0aGF0ICRIXzAkIGNhbm5vdCBiZSByZWplY3RlZC4gVGhlIGNvbmNsdXNpb24gZm9yIGxvZyhGUEkpIGlzIHRoYXQgaXQgaGFzIHVuaXQgcm9vdCAob3IgaXQgaXMgbm90IHN0YXRpb25hcnkpLiBUaGlzIGlzIG5vdCBzdXJwcmlzaW5nIHNpbmNlIHRoZSBvcmlnaW5hbCBGUEkgZGF0YSBoYWQgYSB0cmVuZCBjb21wb25lbnQgKihyZWZlciB0byBkZWNvbXBvc2l0aW9uIHBhcnQgZGlzY3Vzc2VkIGFib3ZlKS4qDQoNCi0gICBGb3IgbG9nIGRpZmZlcmVuY2VkIEZQSSwgbXkgY29uY2x1c2lvbiBpcyB0aGUgb3Bwb3NpdGUgc2luY2UgdGhlIGNhbGN1bGF0ZWQgJHAtdmFsdWUgPCBcYWxwaGEgPSAwLjA1JCBtZWFuaW5nIHRoYXQgJEhfMCQgbXVzdCBiZSBiZSByZWplY3RlZC4gVGhlIGNvbmNsdXNpb24gZm9yIGRpZmYobG9nKEZQSSkpIGlzIHRoYXQgaXQgZG9lcyBub3QgaGF2ZSB1bml0IHJvb3QgKG9yIGl0IGlzIHN0YXRpb25hcnkpLg0KDQpJbiBhYm92ZSBwYXJ0LCBJIGRpc2N1c3NlZCBhYm91dCBGUEkgKGFuZCBsb2coRlBJKSkgZm9sbG93IGEgTUEgcHJvY2VzcyBpbnN0ZWFkIG9mIGFuIEFSIHByb2Nlc3MuIEluIGFkZGl0aW9uIHRvIHRoYXQsIHN0YXRpc3RpY2FsIHRlc3QgcmVzdWx0cyBkaXNjdXNzZWQgaW4gdGhpcyBwYXJ0IGFsc28gc3VnZ2VzdCB0aGF0IHRoZXJlIHdpbGwgYmUgYW4gSSBjb21wb25lbnQgaW4gdGhlIEFSSU1BIHByb2Nlc3MgYmVjYXVzZSB3aXRoIHRoZSBpbnRlZ3JhdGVkIGRpZmZlcmVuY2luZywgZGF0YSBiZWNvbWUgc3RhdGlvbmFyeS4NCg0KIyMgNy4gQVJJTUEgKG9yIFNBUklNQSkgTW9kZWwNCg0KIyMjIDcuMS4gRml0dGluZyB0aGUgbW9kZWwNCg0KQSBnZW5lcmFsIEFSSU1BIG1vZGVsIHJlcXVpcmVzIHRocmVlIHBhcmFtZXRlcnM6DQoNCi0gICBwIGZvciBBUiBjb21wb25lbnQ6IHAgcmVmZXJzIHRvIG51bWJlciBvZiBsYWcgdmFsdWVzIHRvIGluY2x1ZGUgaW4gdGhlIG1vZGVsDQoNCi0gICBkIGZvciBJIGNvbXBvbmVudDogZCByZWZlcnMgdG8gZGlmZmVyZW5jaW5nIHdpbmRvdw0KDQotICAgcSBmb3IgTUEgY29tcG9uZW50OiBxIHJlZmVycyB0byB0aGUgbnVtYmVyIG9mIHBhc3QgcmVzaWR1YWxzIHRvIGluY2x1ZGUgaW4gdGhlIG1vZGVsLg0KDQpSZW1lbWJlciwgaW4gdGhlIGRlY29tcG9zaXRpb24gcGFydCwgSSBkaXNjdXNzZWQgYWJvdXQgdGhlIHN1cnByaXNpbmcgc2Vhc29uYWwgY29tcG9uZW50IG9mIEZQSS4gVGhlcmVmb3JlLCBteSBleHBlY3RhdGlvbiBpcyB0aGF0IHRoZSBmaXR0ZWQgbW9kZWwgd2lsbCBiZSBTQVJJTUEgKHNlYXNvbmFsIEFSSU1BKSwgd2hpY2ggcmVxdWlyZXMgdGhyZWUgYWRkaXRpb25hbCBwYXJhbWV0ZXJzIChQLEQsUSBzYW1lIG1lYW5pbmcgd2l0aCBhYm92ZSBwYXJhbWV0ZXJzIGJ1dCB0aGlzIHRpbWUgc2Vhc29uYWwpLg0KDQpUaGUgb3B0aW1hbCBwYXJhbWV0ZXJzIGNhbiBiZSBkZWNpZGVkIHdpdGggdHJpYWxzIGFuZCB0aGVuIGNvbXBhcmluZyB0aGUgQUlDIG9yIEJJQyB2YWx1ZXMgKHRoZSBsb3dlc3Qgc2hvdWxkIGJlIGNob3Nlbikgb2YgdGhlIG1vZGVscy4gSG93ZXZlciwgYXV0by5hcmltYSgpIGZ1bmN0aW9uIGV4YWN0bHkgZG9lcyB0aGUgc2FtZSB0aGluZyBhbmQgdGhlcmVmb3JlIEkgcHJlZmVyIHRvIHVzZSBpdCB0byBiZSBwcmFjdGljYWwuDQoNCioqKk5vdGU6KiogU2luY2UgZGlmZmVyZW5jaW5nIGNhbiBiZSBoYW5kbGVkIHdpdGhpbiB0aGUgQVJJTUEgKG9yIFNBUklNQSkgcHJvY2VzcyBieSBzZXR0aW5nIGRcPjAsIEkgdXNlZCBsb2coRlBJKSBpbnNpZGUgdGhlIGZ1bmN0aW9uLiBhdXRvLmFyaW1hIHdpbGwgY2hvb3NlIGEgbm9uLXplcm8gdmFsdWUgZm9yIGQgdG8gbWFuYWdlIGRpZmZlcmVuY2luZy4qDQoNCmBgYHtyfQ0KQVJJTUFfZnBpID0gYXV0by5hcmltYShsb2coZnBpKSkNCnN1bW1hcnkoQVJJTUFfZnBpKQ0KYGBgDQoNClNpbWlsYXIgdG8gbXkgZGlzY3Vzc2lvbiBpbiBmb3JtZXIgc2VjdGlvbnMgb2YgdGhpcyBwYXBlciwgYXV0by5hcmltYSgpIGZ1bmN0aW9uIHJlc3VsdGVkIGluIGEgU0FSSU1BKDAsMiwxKSgwLDAsMSkgbW9kZWwuIEFzIGRpc2N1c3NlZCBhYm92ZSwNCg0KLSAgIFRoZXJlIGlzIG5vIEFSIGNvbXBvbmVudA0KDQotICAgVGhlcmUgaXMgSSBjb21wb25lbnQgd2l0aCBkID0gMg0KDQotICAgVGhlcmUgaXMgTUEgY29tcG9uZW50IHdpdGggcSA9IDENCg0KLSAgIFRoZXJlIGlzIHNlYXNvbmFsIE1BIHdpdGggUSA9IDENCg0KIyMgOC4gTW9kZWwgRGlhZ25vc3RpY3MNCg0KYGBge3J9DQp0c2RpYWcoQVJJTUFfZnBpKQ0KYGBgDQoNClRoZSByZXNpZHVhbCBwbG90IG9mIHRoZSBmaXR0ZWQgbW9kZWwgaW5kaWNhdGVzIHNpZ25zIG9mIG5vbi1jb25zdGFudCB2YXJpYW5jZSBzaW5jZSB0aGVyZSBhcmUgcGlja3MgaW4gdGhlIHJlc2lkdWFscyBpbiBzb21lIHBlcmlvZHMgY29tcGFyZWQgdG8gb3RoZXIgcGVyaW9kcy4gVGhpcyBzaG93cyB0aGUgdmFyaWF0aW9uIGluIHRoZSByZXNpZHVhbHMgYXJlIG5vdCBjb25zdGFudC4NCg0KYGBge3J9DQpjaGVja3Jlc2lkdWFscyhBUklNQV9mcGksIHRlc3QgPSBGQUxTRSkNCmBgYA0KDQpUaGUgQUNGIG9mIHJlc2lkdWFscyBsb29rIGFjY2VwdGFibGUgYW5kIHRoZSBkaXN0cmlidXRpb24gb2YgcmVzaWR1YWxzIGlzIGNsb3NlIHRvIG5vcm1hbCBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KYXV0b3Bsb3QoQVJJTUFfZnBpKQ0KYGBgDQoNCkFsbCB0aGUgZG90cyBhcmUgaW5zaWRlIHRoZSBjaXJjbGUsIHdoaWNoIGlzIGFuIGluZGljYXRpb24gb2YgYW4gYWNjZXB0YWJsZSBtb2RlbC4NCg0KYGBge3J9DQphcmNoVGVzdChyZXNpZHVhbHMoQVJJTUFfZnBpKSkNCmBgYA0KDQpJbiBvcmRlciB0byB0ZXN0IGZvciBoZXRlcm9zY2VkYXN0aWNpdHksIEkgdXNlZCBhcmNoVGVzdCBmcm9tIE1UUyBwYWNrYWdlICh3aGljaCB1c2VzIExqdW5nLUJveCB0ZXN0KS4gVGhlICRwLXZhbHVlJCBvZiB0aGUgcmFuay1iYXNlZCB0ZXN0IGlzIGdyZWF0ZXIgdGhhbiAkXGFscGhhID0gMC4wNSQgYnV0IHRoZSBub3JtYWwgdGVzdCBoYXMgYSAkcC12YWx1ZSA8IFxhbHBoYSA9IDAuMDUkLlwNCiQkDQpcYmVnaW57YWxpZ24qfSANCkhfMDogJiBcdGV4dHtObyBhdXRvY29ycmVsYXRpb24gb2YgcmVzaWR1YWwgc3F1YXJlcyAoUmVzaWR1YWxzIGFyZSByYW5kb20pfSBcXA0KSF9BOiAmIFx0ZXh0e0F1dG9jb3JyZWxhdGlvbiBvZiByZXNpZHVhbCBzcXVhcmVzIChSZXNpZHVhbHMgYXJlIE5PVCByYW5kb20pfQ0KXGVuZHthbGlnbip9DQokJA0KDQpBY2NvcmRpbmcgdG8gdGhlIHBhY2thZ2UgZGVmaW5pdGlvbiBmb3IgYXJjaFRlc3QoKSwgdGhlIHJhbmsgc2VyaWVzIG9mIHRoZSBzcXVhcmVkIHRpbWUgc2VyaWVzIGlzIHRoYW4gdXNlZCB0byB0ZXN0IHRoZSBjb25kaXRpb25hbCBoZXRlcm9zY2VkYXN0aWNpdHkuIE91ciBtb2RlbCBpcyBzdWNjZXNzZnVsIGluIHRlcm1zIG9mIHJhbmstYmFzZWQgdGVzdCBtb2RlbCBhbmQgdGhlcmVmb3JlLCB3ZSBjYW4gbWFrZSBmb3JlY2FzdHMgYmFzZWQgb24gdGhlIGV4aXN0aW5nIG1vZGVsLiBIb3dldmVyLCB0aGUgcC12YWx1ZXMgYXJlIHZlcnkgY2xvc2UgdG8gdGhlIGNyaXRpY2FsIGxldmVscyBhbmQgdGhlcmVmb3JlLCB0aGVyZSBpcyBhIG5lZWQgZm9yIEFSQ0ggb3IgR0FSQ0ggbW9kZWxzIHRvIHByZWRpY3QgdGhlIHZhcmlhbmNlLg0KDQojIEZvcmVjYXN0aW5nDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpmcGlfZm9yZWNhc3QgPSBmb3JlY2FzdChBUklNQV9mcGksIGggPSAxMiwgbGV2ZWwgPSBjKDk1LDk5KSkNCg0KZnBpX2ZvcmVjYXN0JHggPSBleHAoZnBpX2ZvcmVjYXN0JHgpDQpmcGlfZm9yZWNhc3QkbWVhbiA9IGV4cChmcGlfZm9yZWNhc3QkbWVhbikNCmZwaV9mb3JlY2FzdCRsb3dlciA9IGV4cChmcGlfZm9yZWNhc3QkbG93ZXIpDQpmcGlfZm9yZWNhc3QkdXBwZXIgPSBleHAoZnBpX2ZvcmVjYXN0JHVwcGVyKQ0KDQojIHVzaW5nIHBsb3RseSBpbiBSIHRvIHBsb3QgaW50ZXJhY3RpdmUgdmlzdWFscy4gDQpwbG90X2ZvcmVjYXN0KGZwaV9mb3JlY2FzdCwgDQogICAgICAgICAgICAgIHRpdGxlID0gJ0Zvb2QgUHJpY2UgSW5kZXggRm9yZWNhc3QgZm9yIDIwMjMnLCANCiAgICAgICAgICAgICAgWHRpdGxlID0gJ1RpbWUnLCANCiAgICAgICAgICAgICAgWXRpdGxlID0gJ0Zvb2QgUHJpY2UgSW5kZXgnLCANCiAgICAgICAgICAgICAgY29sb3IgPSBOVUxMLCANCiAgICAgICAgICAgICAgd2lkdGggPSAyKQ0KYGBgDQoNCiMgQ29uY2x1c2lvbg0KDQpDYW5hZGEncyBDUEkgZm9yIGZvb2QgKG9yIEZQSSkgZm9sbG93cyBhIHNlYXNvbmFsIEFSSU1BIHByb2Nlc3MuIEZvbGxvd2luZyB0aGUgdGVzdHMgaW1wbGVtZW50ZWQgdG8gZW5zdXJlIG1lZXRpbmcgdGhlIGFzc3VtcHRpb25zLCBJIGZpdHRlZCBhIHNlYXNvbmFsIEFSSU1BIG1vZGVsIHRvIGZvcmVjYXN0IEZQSSB2YWx1ZXMgZm9yIDIwMjMuIE15IGZvcmVjYXN0cyBpbmRpY2F0ZXMgdGhhdCBmb29kIHByaWNlcyBpbiBDYW5hZGEgYXJlIGxpa2VseSB0byBpbmNyZWFzZSBmdXJ0aGVyIGluIDIwMjMuIEZ1cnRoZXJtb3JlLCBJIHBsYW4gdG8gZml0IGFuIEFSQ0ggb3IgR0FSQ0ggbW9kZWwgZm9yIHRoZSBjb25kaXRpb25hbCBoZXRlcm9zY2VkYXN0aWNpdHkgYXMgdGhlIG5leHQgc3RlcCBvZiB0aGlzIHBhcGVyLg0KDQojIFJlZmVyZW5jZXMNCg0KUm9zZXIsIE1heCwgYW5kIEhhbm5haCBSaXRjaGllLiAoMjAyMSkgKkZvb2QgUHJpY2VzKi4gKk91ciBXb3JsZCBpbiBEYXRhKiwgKm91cndvcmxkaW5kYXRhLm9yZyouIEF2YWlsYWJsZSBhdDogPGh0dHBzOi8vb3Vyd29ybGRpbmRhdGEub3JnL2Zvb2QtcHJpY2VzLj4gKEFjY2Vzc2VkOiBKYW51YXJ5IDE4LCAyMDIzKS4NCg0KQm9nbWFucywgQy4sIFBlc2NhdG9yaSwgQS4gYW5kIFByaWZ0aSwgRS4gKDIwMjEpICpGb3VyIGZhY3RzIGFib3V0IHNvYXJpbmcgY29uc3VtZXIgZm9vZCBwcmljZXMqLCAqSU1GKi4gQXZhaWxhYmxlIGF0OiA8aHR0cHM6Ly93d3cuaW1mLm9yZy9lbi9CbG9ncy9BcnRpY2xlcy8yMDIxLzA2LzI0L2ZvdXItZmFjdHMtYWJvdXQtc29hcmluZy1jb25zdW1lci1mb29kLXByaWNlcz4gKEFjY2Vzc2VkOiBKYW51YXJ5IDE4LCAyMDIzKS4NCg0KRnJhZGVsbGEsIEEuICgyMDIyKSAqQmVoaW5kIHRoZSBOdW1iZXJzOiBXaGF0J3MgQ2F1c2luZyBHcm93dGggaW4gRm9vZCBQcmljZXMsKiBHb3Zlcm5tZW50IG9mIENhbmFkYSwgU3RhdGlzdGljcyBDYW5hZGEuIEF2YWlsYWJsZSBhdDogPGh0dHBzOi8vd3d3MTUwLnN0YXRjYW4uZ2MuY2EvbjEvcHViLzYyZjAwMTRtLzYyZjAwMTRtMjAyMjAxNC1lbmcuaHRtPiAoQWNjZXNzZWQ6IEphbnVhcnkgMTgsIDIwMjMpLg0KDQpTdGF0aXN0aWNzIENhbmFkYSAoMjAyMykgKkNvbnN1bWVyIHByaWNlIGluZGV4LCBtb250aGx5LCBzZWFzb25hbGx5IGFkanVzdGVkKiwgKkNvbnN1bWVyIFByaWNlIEluZGV4LCBtb250aGx5LCBzZWFzb25hbGx5IGFkanVzdGVkKi4gR292ZXJubWVudCBvZiBDYW5hZGEsIFN0YXRpc3RpY3MgQ2FuYWRhLiBBdmFpbGFibGUgYXQ6IDxodHRwczovL3d3dzE1MC5zdGF0Y2FuLmdjLmNhL3QxL3RibDEvZW4vdHYuYWN0aW9uP3BpZD0xODEwMDAwNjAxPiAoQWNjZXNzZWQ6IEphbnVhcnkgMTgsIDIwMjMpLg0KDQpTdGF0aXN0aWNzIENhbmFkYSAoMjAyMikgKlN0YXRpc3RpY3MgQ2FuYWRhIE9wZW4gTGljZW5jZSosICpHb3Zlcm5tZW50IG9mIENhbmFkYSwgU3RhdGlzdGljcyBDYW5hZGEqLiBHb3Zlcm5tZW50IG9mIENhbmFkYSwgU3RhdGlzdGljcyBDYW5hZGEuIEF2YWlsYWJsZSBhdDogPGh0dHBzOi8vd3d3LnN0YXRjYW4uZ2MuY2EvZW4vcmVmZXJlbmNlL2xpY2VuY2U+IChBY2Nlc3NlZDogSmFudWFyeSAxOCwgMjAyMykuDQoNClN0YXRpc3RpY3MgQ2FuYWRhICgyMDIyKSAqV2ViIGRhdGEgc2VydmljZSAoV0RTKSosICpHb3Zlcm5tZW50IG9mIENhbmFkYSwgU3RhdGlzdGljcyBDYW5hZGEqLiBHb3Zlcm5tZW50IG9mIENhbmFkYSwgU3RhdGlzdGljcyBDYW5hZGEuIEF2YWlsYWJsZSBhdDogPGh0dHBzOi8vd3d3LnN0YXRjYW4uZ2MuY2EvZW4vZGV2ZWxvcGVycy93ZHM+IChBY2Nlc3NlZDogSmFudWFyeSAxOCwgMjAyMykuDQo=